namespace Warehouse

open System.Collections.ObjectModel
open System.Data
open System.Windows
open System
open System.IO
open System.Configuration
open System.Linq
open Microsoft.FSharp.Reflection
open Microsoft.VisualBasic

open FSharp.Windows

open Warehouse.Controls

type ProductController(?repository) = 
    inherit Controller<ProductEvents, ProductModel>()

    let repository = defaultArg repository (new DataModel.Repository())

    let createUpc() = 
        let upc : UpcModel = Model.Create()
        upc.Note <- repository.NewPackage
        upc.Code <- repository.GenerateUpc()
        upc

    override this.InitModel model =
        if model.Brands = null then model.Brands <- repository.Brands |> Seq.toArray |> Array.sortBy(fun x -> x.Name)
        if model.Categories = null then model.Categories <- repository.Categories |> Seq.toArray |> Array.sortBy(fun x -> x.Name)
        if model.UnitLookup = null then model.UnitLookup <- repository.Units.ToArray()
        if model.UpcNotes = null then model.UpcNotes <- repository.UpcNotes.ToArray()
        if model.UPCs = null || Seq.isEmpty model.UPCs
        then 
            assert model.IsNewEntity
            model.UPCs <- createUpc() |> ObservableCollection.singleton
        if model.IsNewEntity 
        then 
            model.Unit <- repository.Oz

    override this.Dispatcher = Sync << function 
        | Save ok -> this.Save ok
        | SizeOrColorChanging(newValue, cancel) -> this.SizeOrColorChanging newValue cancel
        | AddUpc -> this.AddUpc
        | UnitChanged -> this.UnitChanged
        | SetAsSKU -> this.SetAsSKU
        | AddCategory -> this.AddCategory

    member this.Save ok model = 
        this.ValidateBeforeSave model
        if model.IsValid then
            if model.ProductName = null 
            then
                model.ProductName <- DataModel.ProductName(
                    Name = model.SelectedProductName.TrimEnd(), 
                    Brand = model.SelectedBrand,
                    Category = model.SelectedCategory)
                //repository.AddToProductNames model.ProductName

            for upc in model.UPCs do upc.Code <- upc.Code.Trim()

            if model.IsNewEntity then
                assert (model.SKU = null)
                model.SKU <- model.UPCs.[0].Code

            model.UpdateEntity()
            model.SaveEntity repository
            model.Compute()

            model.UpdateEntity()
            model.SaveEntity repository

            ok()

    member this.ValidateBeforeSave model =
        model.ClearAllErrors()
        model |> Validation.objectRequired  <@ fun m -> m.SelectedBrand @>
        model |> Validation.objectRequired <@ fun m -> m.SelectedProductName @>
        model |> Validation.positive <@ fun m -> m.ShopCost @>

        assert (model.Unit <> null)
        if model.Unit.IsGallon() then model.SizeOrColor <- "1"
        if model.Unit.IsSizeOrColorRequired() && String.IsNullOrEmpty model.SizeOrColor
        then
            model |> Validation.setError <@ fun m -> m.SizeOrColor @> "Size/color is mandatory field."

        for upc in model.UPCs do
            upc |> Validation.textRequired <@ fun m -> m.Code @>
            upc |> Validation.objectRequired <@ fun m -> m.Note @>
            if upc.IsNewEntity 
                && repository.UpcExists upc.Code then
                let message = sprintf "Code %s already exists in database" upc.Code
                upc |> Validation.setError <@ fun m -> m.Code @> message

        if model.SKU <> null && not(model.UPCs |> Seq.exists(fun upc -> upc.Code = model.SKU)) 
        then
            model |> Validation.setError <@ fun m -> m.SKU @> "SKU has to be equal to one of UPC codes."

    member this.SizeOrColorChanging newValue cancel model =
        if model.Unit.Name.ToLower() <> "color" && not (Decimal.canBeParsedFrom newValue) 
        then
            cancel()

    member this.AddUpc model = 
        createUpc() |> model.UPCs.Add 

    member this.UnitChanged model =
        if not(model.Unit.IsSizeOrColorRequired()) then model.SizeOrColor <- null

    member this.SetAsSKU model = 
        model.SKU <- model.SelectedUpc.Code

    member this.AddCategory model =  
        match Interaction.InputBox("Enter Category Name", Title = "New Category") with
        | "" -> ()
        | name -> 
            let category = DataModel.Category(Name = name)
            repository.AddToCategories category
            let affectedRecords = repository.SaveChanges()
            assert (affectedRecords > 0)
            model.Categories <- repository.Categories |> Seq.toArray |> Array.sortBy(fun x -> x.Name)
